﻿using ArchestrA.Client.RuntimeData;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Threading;

namespace ArchestrA.Apps.AttributeBrowser2
{
    public class aaObject : INotifyPropertyChanged
    {
        public string Name { get; set; }
        private List<aaAttribute> Attributes { get; set; } = new List<aaAttribute>();
        public ObservableRangeCollection<aaAttribute> FilteredAttributes { get; set; }
        //public List<DataReferenceSource> Sources { get; set; } = new List<DataReferenceSource>();
        //public List<DataReference> References { get; set; } = new List<DataReference>();
        private List<DataItem> Items { get; set; } = new List<DataItem>(); //currently subscribed items
        public DataSubscription DataSubscription { get; set; }
        public Filter FilterOptions { get; set; } = Filter.HasInput | Filter.IsCalculation | Filter.IsProcessValue | Filter.IsStatus;
        public event PropertyChangedEventHandler PropertyChanged;
        private Dispatcher _d;
        public event Action DescriptionUpdated;

        public void SubscribeAttributes()
        {
            _d.Invoke(() => FilteredAttributes.Clear());
            Attributes.Clear();
            DataReferenceSource r = new DataReferenceSource(
                Name + "._Attributes[]",
                Name,
                (i) =>
                {
                    string[] attributes = (string[])i.VTQ.Value.Array;
                    new Thread(() =>
                    {
                        Thread.CurrentThread.IsBackground = true;
                        DealWithNewAttributes(attributes.ToList<string>());
                    }).Start();

                }
                );
            if (DataSubscription != null)
                DataSubscription.Subscribe(new DataReferenceSource[] { r });
        }

        private void DealWithNewAttributes(List<string> attributes)
        {
            DataSubscription.UnsubscribeAll();
            attributes.Sort();
            Dictionary<string, List<string>> uniqueAttributes = new Dictionary<string, List<string>>();
            string previous = "zoogabooga";//I bet nobody will call an attribute this
            List<string> currentList = new List<string>();
            for (int i = 0; i < attributes.Count; i++)
            {
                if (attributes[i].StartsWith(previous))
                {
                    currentList.Add(attributes[i]);
                }
                else
                {
                    currentList = new List<string>();
                    uniqueAttributes.Add(attributes[i], currentList);
                    previous = attributes[i];
                }

            }
            foreach (var kvp in uniqueAttributes)
            {
                aaAttribute a = new aaAttribute(kvp.Key, kvp.Value, this);
                Attributes.Add(a);
                a.PropertyChanged += A_PropertyChanged;
            }
            ApplyFilter();
            Subscribe();

        }

        private void A_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            DescriptionUpdated?.Invoke();
        }

        private void ApplyFilter()
        {
            _d.Invoke(() =>
            {
                FilteredAttributes.Clear();
                var results = from i in Attributes where (i.Flags & FilterOptions) != 0 select i;
                FilteredAttributes.AddRange(results);
            });
        }

        private void Subscribe()
        {
            foreach (var a in FilteredAttributes)
            {
                Items.AddRange(a.DataItems);
            }

            var dataSources = new List<DataReferenceSource>();
            foreach (var dataItem in Items)
            {
                // Popuplate the list of DataReferenceSource
                // The call back action of each source will directly update the data item collection to update the UI
                dataSources.Add(
                    new DataReferenceSource(
                        dataItem.ReferenceString,
                        dataItem.OwningObject,
                        (d) =>
                        {
                            dataItem.DataReference = d;
                            dataItem.DataValue = d.VTQ.Value.RawValue;
                            dataItem.StatusSetting = d.VTQ.QualityStatus.StatusSetting;
                            dataItem.MxQuality = d.VTQ.QualityStatus.MxQuality;
                            dataItem.OpcUaQuality = d.VTQ.QualityStatus.OPCUAQuality;
                            dataItem.Timestamp = d.VTQ.Timestamp;
                        }));
            }

            // Call Runtime Data SDK API to subscribe all data references in bulk
            DataSubscription.Subscribe(dataSources.ToArray());
        }

        public void Unsubscribe()
        {
            try
            {
                //var result = Items.Select(i => i.DataReference).ToArray();
                DataSubscription.UnsubscribeAll();
                Items.Clear();
                _d.Invoke(() => FilteredAttributes.Clear());
            }
            catch { }
        }

        private void OnProptertyChanged([CallerMemberName] string name = "")
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        public aaObject()
        {
            try
            {
                //for some reason, the Application.Current.Dispatcher is not the thread that the GUI is running on.  so when we are created, we will assume that 
                //whatever the current dispatcher is is the one we want to use.  Seems to work.
                _d = Dispatcher.CurrentDispatcher;
                FilteredAttributes = new ObservableRangeCollection<aaAttribute>();
            }
            catch { }
        }

    }
}
